{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "ace7a54e",
   "metadata": {},
   "source": [
    "<table align=\"left\">\n",
    "  <td>\n",
    "    <a href=\"https://colab.research.google.com/github/nyandwi/machine_learning_complete/blob/main/7_intro_to_artificial_neural_networks_and_tensorflow/2_intro_to_tensorflow_for_deeplearning.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
    "  </td>\n",
    "</table>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a9f9f77",
   "metadata": {},
   "source": [
    "*This notebook was created by [Jean de Dieu Nyandwi](https://twitter.com/jeande_d) for the love of machine learning community. For any feedback, errors or suggestion, he can be reached on email (johnjw7084 at gmail dot com), [Twitter](https://twitter.com/jeande_d), or [LinkedIn](https://linkedin.com/in/nyandwi).*"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "304ad719",
   "metadata": {
    "id": "304ad719"
   },
   "source": [
    "<a name='0'></a>\n",
    "# Intro to TensorFlow for Deep Learning"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b6a095ea",
   "metadata": {
    "id": "b6a095ea"
   },
   "source": [
    "## Contents \n",
    "\n",
    "* [1. What is TensorFlow?](#1)\n",
    "* [2. TensorFlow Model APIs](#2)\n",
    "   * [2.1 Sequential API](#2-1)\n",
    "   * [2.2 Functional API](#2-2)\n",
    "   * [2.3 Model SubClassing API](#2-3)\n",
    "\n",
    "* [3. A Quick Tour into TensorFlow Ecosystem](#3)\n",
    "   * [3.1 Tools](#3-1)\n",
    "   * [3.2 Models and Datasets](#3-2)\n",
    "   * [3.3 Model Deployment](#3-3)\n",
    "\n",
    "* [4. Basics Tensors](#4)\n",
    "   * [4.1 Intro to Tensors](#4-1)\n",
    "   * [4.2 Creating a Tensor with tf.constant()](#4-2)\n",
    "   * [4.3 Creating a Tensor with tf.variable()](#4-3)\n",
    "   * [4.4 Creating A Tensor from Existing Functions](#4-4)\n",
    "   * [4.5 Selecting Data in a Tensor](#4-5)\n",
    "   * [4.6 Performing operations on Tensors](#4-6)\n",
    "   * [4.7 Manipulating a Tensor Shape](#4-7)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4e73d1ca",
   "metadata": {
    "id": "4e73d1ca"
   },
   "source": [
    "<a name='1'></a>\n",
    "\n",
    "## 1. What is TensorFlow"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9c53b146",
   "metadata": {
    "id": "9c53b146"
   },
   "source": [
    "TensorFlow is an open source and an end to end platform used for building machine learning models. Being end to end, you can prepare data, build models, diagnose, improve, and deploy them. \n",
    "\n",
    "TensorFlow uses [Keras](https://keras.io) at its backend. Keras is a well beautifully designed API for building deep learning models in popular fields such as Computer Vision and Natural Language Processing. \n",
    "\n",
    "TensorFlow has got a strong community, from users, learning resources and whole range of technical supports. Not only it powers majority of Google apps such as YouTube, Maps and Google Photos, it is also widely used across startups and other big techs. If you would like to know who is using TensorFlow, [here you go!](https://www.tensorflow.org/about/case-studies).  "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "813fd464",
   "metadata": {
    "id": "813fd464"
   },
   "source": [
    "<a name='2'></a>\n",
    "\n",
    "## 2. TensorFlow Model APIs"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d61aecff",
   "metadata": {
    "id": "d61aecff"
   },
   "source": [
    "TensorFlow being suited for variety of tasks, there are 3 ways to build deep learning models."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "QJHHkIq5q8ru",
   "metadata": {
    "id": "QJHHkIq5q8ru"
   },
   "source": [
    " <a name='2-1'></a> \n",
    " ### 2.1 Sequential API\n",
    "\n",
    "This is the simplest model building option. When building a model, you start from the input to the output, no other way around. This API is suited for tasks that don't require multiple inputs or outputs, or skip connections. \n",
    "\n",
    "Sequential API can be a good API to use for things like image classification or regression tasks. With things like object detection and segmentation, we need another API, which is Functional API. \n",
    "\n",
    "Below is how a sequential model looks like. \n",
    "\n",
    "```\n",
    "# Building a sequential model \n",
    "\n",
    "from tensorflow import keras\n",
    "\n",
    " \n",
    "model = keras.models.Sequential([\n",
    "        \n",
    "        keras.layers.Dense(16, activation='relu'),\n",
    "        keras.layers.Dense(32, activation='relu'),\n",
    "        keras.layers.Dense(1, activation='sigmoid')\n",
    "        \n",
    "])\n",
    "    \n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "GXVRQrbaqx57",
   "metadata": {
    "id": "GXVRQrbaqx57"
   },
   "source": [
    "<a name='2-2'></a>\n",
    "### Functional API\n",
    "\n",
    "This type of API makes it easy to build models that can take multiple inputs/outputs, or skip connections. \n",
    "\n",
    "It is well suited in advanced things like object detection and segmentation. In object detection, there are two main involved things. \n",
    "\n",
    "One is recognizing the object(classification) and other is localizing the object(regression: predicting the bounding boxes coordinates). "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3f996b11",
   "metadata": {
    "id": "3f996b11"
   },
   "source": [
    "\n",
    "![kites_detections_output.jpg](https://cdn.hashnode.com/res/hashnode/image/upload/v1616839653654/-NiIL9N3v.jpeg)\n",
    "*You can't build an object detection model with Sequential API, but Functional API instead!! [Source](https://github.com/tensorflow/models/tree/master/research/object_detection).*"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e158c578",
   "metadata": {},
   "source": [
    "Below is how a functional model looks like.\n",
    "\n",
    "```\n",
    "# Building a same model in Functional API\n",
    "\n",
    "from tensorflow import keras\n",
    "\n",
    "inputs = keras.Input()\n",
    "x = keras.layers.Dense(16, activation='relu')(inputs)\n",
    "x = keras.layers.Dense(32, activation='relu')(x)\n",
    "output = keras.layers.Dense(1, activation='sigmoid')(x)\n",
    "    \n",
    "model = keras.Model(inputs, output)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "49d8b590",
   "metadata": {
    "id": "49d8b590"
   },
   "source": [
    "<a name='2-3'></a>\n",
    "\n",
    "### Model SubClassing\n",
    "\n",
    "SubClassing API is for building custom models and having full control of every step in model building and training. \n",
    "\n",
    "In most cases, Sequential and Functional API will be all you need to build almost anything. \n",
    "\n",
    "Below is how a subclassing model looks like.\n",
    "\n",
    "```\n",
    "# Building a same custom model \n",
    "\n",
    "from tensorflow import keras\n",
    "\n",
    "class MLP(keras.Model):\n",
    "\n",
    "  def __init__(self, **kwargs):\n",
    "    super(MLP, self).__init__(**kwargs)\n",
    "    self.dense_1 = keras.layers.Dense(16, activation='relu')\n",
    "    self.dense_2 = keras.layers.Dense(32, activation='relu')\n",
    "    self.dense_3 = keras.layers.Dense(1, activation='sigmoid')\n",
    "\n",
    "  def call(self, inputs):\n",
    "    x = self.dense_1(inputs)\n",
    "    x = self_dense_2(x)\n",
    "    x = self_dense_3(x)\n",
    "    \n",
    "    return x\n",
    "\n",
    "# Instantiate the model\n",
    "\n",
    "mlp = MLP()\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3169f9de",
   "metadata": {
    "id": "3169f9de"
   },
   "source": [
    "<a name='3'></a>\n",
    "\n",
    "## 3. A Quick Tour into TensorFlow Ecosystem\n",
    "\n",
    "Beside community, one of my favourite things I like about TensorFlow is its whole ecosystem of tools and resources. Below is a list of tools, libraries, and resources that are built to simplify things. "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "m55g-rJqu_N7",
   "metadata": {
    "id": "m55g-rJqu_N7"
   },
   "source": [
    "<a name='3-1'></a>\n",
    "\n",
    "### Tools\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f5656e9c",
   "metadata": {
    "id": "f5656e9c"
   },
   "source": [
    "#### 1. Colaboratory\n",
    "\n",
    "[Colaboratory](https://colab.research.google.com/notebooks/intro.ipynb) is a free web version of jupyter notebook that doesn't require any set up. You can import almost any library without having to install it. \n",
    "\n",
    "#### 2. TensorBoard\n",
    "\n",
    "[TensorBoard](https://tensorboard.dev/) provides the engineers and scientists with the visualizations and debugging capabilities for their Machine Learning models. \n",
    "\n",
    "With TensorBoard, you can:\n",
    "\n",
    "   * Track and visualize the loss and accuracy metrics\n",
    "   * View the weights, biases, and other parameters in plots format such as histogram or line plot\n",
    "   * Display image, audio, and text data\n",
    "   * See which hyperparameters might be a good match for the model to converge\n",
    "   \n",
    "\n",
    "![Tensorboard.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1616575702847/t9hohDbAY.png)\n",
    "\n",
    "#### 3. What If Tool(WIT)\n",
    "\n",
    "What if you wanted a visual way to understand your data and model within your environment? [WIT](https://pair-code.github.io/what-if-tool/get-started/) is exactly designed for that purpose. It can help you to understand your dataset and the output of your model.\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "UfIAMd7OvMaW",
   "metadata": {
    "id": "UfIAMd7OvMaW"
   },
   "source": [
    "<a name='3-2'></a>\n",
    "\n",
    "### Models and Datasets\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "grmqH29ZvKIo",
   "metadata": {
    "id": "grmqH29ZvKIo"
   },
   "source": [
    "\n",
    "#### 1. TensorFlow Hub\n",
    "\n",
    "[TensorFlow Hub](https://tfhub.dev/) is a collection of ready to use models in variety of tasks, from image recognition, object detection, image segmentation, sound recognition, text classification, etc..\n",
    "\n",
    "#### 2. Model Garden\n",
    "\n",
    "Model Garden is an [official GitHub repository of TensorFlow models](https://github.com/tensorflow/models/tree/master/official), build with high-level APIs. The models available are Computer Vision, Natural Language Processing, and recommendation.\n",
    "\n",
    "#### 3. TensorFlow Datasets (TFDS)\n",
    "\n",
    "[TFDS](https://www.tensorflow.org/tfds) contains datasets of different types, from images, texts, video, to sounds...We will be using many TFDS datasets in our deep learning quest. \n",
    "\n",
    "\n",
    "\n",
    "\n",
    "**************\n",
    "\n",
    "There are so many tools and functionalities that are provided by TensorFlow. As much as we can, we will try to leverage these tools. \n",
    "\n",
    "This was a quick walkthrough the TensorFlow ecosystem. If you would like to learn more, go to [tensorflow website](https://www.tensorflow.org/resources/libraries-extensions) or check out this [article](https://jeande.tech/tf-ecosystem) that I wrote a while back. "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "S57Ak5zavZn_",
   "metadata": {
    "id": "S57Ak5zavZn_"
   },
   "source": [
    "\n",
    "<a name='3-3'></a>\n",
    "### Models Deployment "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "GU3Ht5UavXpI",
   "metadata": {
    "id": "GU3Ht5UavXpI"
   },
   "source": [
    "\n",
    "\n",
    "#### 1. TensorFlow Extended (TFX)\n",
    "\n",
    "[TFX](https://www.tensorflow.org/tfx/) is an end to end platform for creating machine learning pipelines. With TFX, you can prepare data, train models, validate them, and deploy them in production environments.\n",
    "\n",
    "#### 2. TensorFlow.js\n",
    "\n",
    "[TensorFlow.js](https://www.tensorflow.org/js/) makes it easy to train and deploy model in web browsers. \n",
    "\n",
    "#### 3. TensorFlow Lite \n",
    "\n",
    "[TF Lite](https://www.tensorflow.org/lite/) makes it possible to train, deploy and optimize models in mobile devices and microcontrollers. "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cdc613d1",
   "metadata": {
    "id": "cdc613d1"
   },
   "source": [
    "<a name='4'></a>\n",
    "\n",
    "## 4. The Basics of Tensors"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0jKEvJIzmaF9",
   "metadata": {
    "id": "0jKEvJIzmaF9"
   },
   "source": [
    "<a name='4-1'></a>\n",
    "### 4.1 Intro to Tensors"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1F4EWUAmB449",
   "metadata": {
    "id": "1F4EWUAmB449"
   },
   "source": [
    "A tensor is a multidimensional array of the same data type. A tensor can be a scalar (single number), a vector, or a matrix. \n",
    "\n",
    "If you have used NumPy, tensors are like NumPy arrays, except that tensors have GPU(Graphical Processing Unit) support. \n",
    "\n",
    "A typical tensor has the following information:\n",
    "\n",
    "* **Shape**: The length or number of elements of each of the tensor dimension/axes.\n",
    "* **Rank**: The number of dimensions/axes in a tensor. A scalar tensor (a single number) has rank 0, a vector has a rank 1 (a vector is a 1D), and a matrix has rank 2 (or 2D). \n",
    "* **Axis/Dimension**: This is a particular dimension of a tensor\n",
    "* **Size**: This is the total number of items in the tensor. \n",
    "\n",
    "But why tensor/NumPy array things? \n",
    "\n",
    "Well, almost all types of data ca be represented as an array of numbers. Take an example: \n",
    "\n",
    "* Image can be represented as an array of pixels. \n",
    "* Any text data can be converted into an array of numbers (or tokens representing words)\n",
    "* Video (made of sequence of images) can be represented as an array of numbers. \n",
    "\n",
    "Having the ability to convert these raw data into tensors/arrays make it easy to preprocess it, either when performing conventional numerical computations or when it is the data we are preparing to feed to a machine learning model. Take a simple example, we can not feed a raw text to a machine learning model. That text has to be converted into numbers. \n",
    "\n",
    "That's is for the basic intro to bensors. For more about tensors, check out TensorFlow's [introduction to tensors](https://www.tensorflow.org/guide/tensor), or this [rich wikipedia tensor page](https://en.wikipedia.org/wiki/Tensor). \n",
    "\n",
    "In later parts, we will see how to:\n",
    "\n",
    "* Create a tensor with tf.constant()\n",
    "* Create a tensor with tf.variable()\n",
    "* Create tensors from existing functions\n",
    "* Select data in a tensor\n",
    "* Perform operations in tensor\n",
    "* Manipulate tensor shape"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1GURLW8gqVPn",
   "metadata": {
    "id": "1GURLW8gqVPn"
   },
   "source": [
    "<a name='4-2'></a>\n",
    "\n",
    "### 4. 2 Creating a Tensor with tf.constant()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "nhP9rgeTIPJX",
   "metadata": {
    "id": "nhP9rgeTIPJX"
   },
   "source": [
    "A Tensor can be a scalar, a vector or a matrix. \n",
    "Let's use `tf.constant()` to create these tensor types. \n",
    "\n",
    "A tensor created with `tf.constant()` is immutable. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3iB4EL10JLZP",
   "metadata": {
    "id": "3iB4EL10JLZP"
   },
   "outputs": [],
   "source": [
    "# I will first import tensorflow as tf\n",
    "# Also import numpy as np\n",
    "# If you are using Colab, no need to install them \n",
    "\n",
    "import tensorflow as tf\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "hLoQKlElqXBy",
   "metadata": {
    "id": "hLoQKlElqXBy"
   },
   "outputs": [],
   "source": [
    "# Creating a scalar tensor\n",
    "# You can specify dtype but TF will detect its if left unspecified\n",
    "\n",
    "scalar_tensor = tf.constant(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "XbiXNElzJy0R",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "XbiXNElzJy0R",
    "outputId": "2e21dc07-4bc3-4a8f-87c6-5ed9bd1c8fea"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tf.Tensor(10, shape=(), dtype=int32)\n"
     ]
    }
   ],
   "source": [
    "# Displaying created tensor\n",
    "\n",
    "print(scalar_tensor)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "tcbvOMdbJ4Pu",
   "metadata": {
    "id": "tcbvOMdbJ4Pu"
   },
   "outputs": [],
   "source": [
    "# We can also create a vector or rank 1 tensor\n",
    "# Simply put, a vector is one dimensional\n",
    "# We can create it from a list of values\n",
    "\n",
    "vect_tensor = tf.constant([1.0,2.0,3.0,4.0,5.0,6.0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "JnBdyNcNKtPZ",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "JnBdyNcNKtPZ",
    "outputId": "af30c3f1-9f54-4272-b063-57e46929adba"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tf.Tensor([1. 2. 3. 4. 5. 6.], shape=(6,), dtype=float32)\n"
     ]
    }
   ],
   "source": [
    "print(vect_tensor)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "kx-SdOE1Ky37",
   "metadata": {
    "id": "kx-SdOE1Ky37"
   },
   "source": [
    "A vector of 1 dimensional values was created. As you can see, the data type is `float32` because the values were floats. TensorFlow detects that automatically from values if the data type was not mentioned. \n",
    "\n",
    "Let's now create a tensor with rank 2 or two dimensions. This is actually a matrix. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "KSuWT0_aLE-_",
   "metadata": {
    "id": "KSuWT0_aLE-_"
   },
   "outputs": [],
   "source": [
    "mat_tensor = tf.constant([[2,4],\n",
    "                         [6,8],\n",
    "                         [10,12]], dtype=tf.int32)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "twT8PtbBLrVA",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "twT8PtbBLrVA",
    "outputId": "b9828429-cc93-4cc9-e2f0-e1134c38166c"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tf.Tensor(\n",
      "[[ 2  4]\n",
      " [ 6  8]\n",
      " [10 12]], shape=(3, 2), dtype=int32)\n"
     ]
    }
   ],
   "source": [
    "print(mat_tensor)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "AS-4T0fdL7KU",
   "metadata": {
    "id": "AS-4T0fdL7KU"
   },
   "source": [
    "If you can see in the displayed tensor above, the shape is `(3,2)` which means our tensor has 3 rows and 2 columns. \n",
    "\n",
    "You can also check the number of dimensions or axes of a tensor using `tensor_name.ndim`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "luy2-ckdM30Y",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "luy2-ckdM30Y",
    "outputId": "f3c2be5f-3993-4ca7-817a-d6d8988fad75"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 14,
     "metadata": {
      "tags": []
     },
     "output_type": "execute_result"
    }
   ],
   "source": [
    "scalar_tensor.ndim"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9OZViVpSNz0D",
   "metadata": {
    "id": "9OZViVpSNz0D"
   },
   "source": [
    "A scalar tensor does not have any dimension. It's just a single value. But if we do the same thing for a vector or matrix, you will see something different. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "OatUK_LaN0Th",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "OatUK_LaN0Th",
    "outputId": "7b95aa53-c557-49f5-f543-936e04be3c47"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 16,
     "metadata": {
      "tags": []
     },
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# A vector has 1 dimension\n",
    "\n",
    "vect_tensor.ndim"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "JeKe3q3qOHmW",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "JeKe3q3qOHmW",
    "outputId": "819c762e-8d04-41f3-d446-f4888f5cb9e8"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": 17,
     "metadata": {
      "tags": []
     },
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# A matrix has 2D or more dimensions\n",
    "\n",
    "mat_tensor.ndim"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "CEwg8oCLO0Fm",
   "metadata": {
    "id": "CEwg8oCLO0Fm"
   },
   "source": [
    "Just like NumPy array, a tensor can have many dimensions. Let's create a tensor with 3 dimensions. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bS1ad5oZO0dr",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "bS1ad5oZO0dr",
    "outputId": "ac1c4dd0-a5c3-4b03-fb86-3eba8cdca2ef"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tf.Tensor(\n",
      "[[[1 2 3 4 5]\n",
      "  [6 7 8 9 8]]\n",
      "\n",
      " [[1 3 5 7 9]\n",
      "  [2 4 6 8 1]]\n",
      "\n",
      " [[1 2 3 5 4]\n",
      "  [3 4 5 6 7]]], shape=(3, 2, 5), dtype=int32)\n"
     ]
    }
   ],
   "source": [
    "tensor_3d = tf.constant([\n",
    "                         [[1,2,3,4,5],\n",
    "                         [6,7,8,9,8]],\n",
    "                         [[1,3,5,7,9],\n",
    "                         [2,4,6,8,1]],\n",
    "                         [[1,2,3,5,4],\n",
    "                         [3,4,5,6,7]], ])\n",
    "\n",
    "print(tensor_3d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "Ilvi_ojDPdPZ",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "Ilvi_ojDPdPZ",
    "outputId": "6965884c-89d8-4148-c4bf-5e2384ddc31c"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 31,
     "metadata": {
      "tags": []
     },
     "output_type": "execute_result"
    }
   ],
   "source": [
    "tensor_3d.ndim"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "PcCwP51-TDBI",
   "metadata": {
    "id": "PcCwP51-TDBI"
   },
   "source": [
    "A tensor can be converted into NumPy array by calling `tensor_name.numpy` or `np.array(tensor_name)`. \n",
    "\n",
    "TensorFlow plays well with NumPy. And if not yet done, TensorFlow recently posted that they are working on gettint the whole of NumPy into TensorFlow. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "-5sc7JV8Tv_C",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "-5sc7JV8Tv_C",
    "outputId": "3df93113-da2c-46e0-f636-f370c1e71e43"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[[1, 2, 3, 4, 5],\n",
       "        [6, 7, 8, 9, 8]],\n",
       "\n",
       "       [[1, 3, 5, 7, 9],\n",
       "        [2, 4, 6, 8, 1]],\n",
       "\n",
       "       [[1, 2, 3, 5, 4],\n",
       "        [3, 4, 5, 6, 7]]], dtype=int32)"
      ]
     },
     "execution_count": 35,
     "metadata": {
      "tags": []
     },
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Converting a tensor into a NumPy array\n",
    "\n",
    "n_array = tensor_3d.numpy()\n",
    "\n",
    "n_array"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "aZcE2h4UT674",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "aZcE2h4UT674",
    "outputId": "185c8baf-884c-49ab-a4b7-d0fd4f41dc6b"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[[1, 2, 3, 4, 5],\n",
       "        [6, 7, 8, 9, 8]],\n",
       "\n",
       "       [[1, 3, 5, 7, 9],\n",
       "        [2, 4, 6, 8, 1]],\n",
       "\n",
       "       [[1, 2, 3, 5, 4],\n",
       "        [3, 4, 5, 6, 7]]], dtype=int32)"
      ]
     },
     "execution_count": 37,
     "metadata": {
      "tags": []
     },
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Using np.array(tensor_name)\n",
    "\n",
    "np.array(tensor_3d)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "IYwTQS4zUq9J",
   "metadata": {
    "id": "IYwTQS4zUq9J"
   },
   "source": [
    "<a name='4-3'></a>\n",
    "\n",
    "### 4. 3 Creating a Tensor with tf.Variable()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9U0iKXNXU7n9",
   "metadata": {
    "id": "9U0iKXNXU7n9"
   },
   "source": [
    "A tensor created with `tf.constant()` is immutable, it can not be changed. Such kind of tensor can not be used as weights in neural networks because they need to be changed/updated in backpropogation for example. \n",
    "\n",
    "With `tf.Variable()`, we can create tensors that can be mutable and thus can be used in things like updating the weights of neural networks like said above. \n",
    "\n",
    "Creating variable tensor is as simple as the former. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "_w7GOGQWWAOG",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "_w7GOGQWWAOG",
    "outputId": "50219395-65fa-492f-a888-6f6c81d74046"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<tf.Variable 'Variable:0' shape=(3, 2, 5) dtype=int32, numpy=\n",
      "array([[[1, 2, 3, 4, 5],\n",
      "        [6, 7, 8, 9, 8]],\n",
      "\n",
      "       [[1, 3, 5, 7, 9],\n",
      "        [2, 4, 6, 8, 1]],\n",
      "\n",
      "       [[1, 2, 3, 5, 4],\n",
      "        [3, 4, 5, 6, 7]]], dtype=int32)>\n"
     ]
    }
   ],
   "source": [
    "var_tensor = tf.Variable([\n",
    "                         [[1,2,3,4,5],\n",
    "                         [6,7,8,9,8]],\n",
    "                         [[1,3,5,7,9],\n",
    "                         [2,4,6,8,1]],\n",
    "                         [[1,2,3,5,4],\n",
    "                         [3,4,5,6,7]], ])\n",
    "\n",
    "print(var_tensor)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2svEn-TnWr5t",
   "metadata": {
    "id": "2svEn-TnWr5t"
   },
   "source": [
    "It can also be converted to NumPy array, just like tensors created with `tf.constant()`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "xZVFZhRYWsbv",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "xZVFZhRYWsbv",
    "outputId": "bb273948-a7ed-495a-9ba5-27994d625f84"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[[1, 2, 3, 4, 5],\n",
       "        [6, 7, 8, 9, 8]],\n",
       "\n",
       "       [[1, 3, 5, 7, 9],\n",
       "        [2, 4, 6, 8, 1]],\n",
       "\n",
       "       [[1, 2, 3, 5, 4],\n",
       "        [3, 4, 5, 6, 7]]], dtype=int32)"
      ]
     },
     "execution_count": 42,
     "metadata": {
      "tags": []
     },
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Converting a variable tensor into NumPy array\n",
    "\n",
    "var_tensor.numpy()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "lBINI0X2W7s7",
   "metadata": {
    "id": "lBINI0X2W7s7"
   },
   "source": [
    "<a name='4-4'></a>\n",
    "\n",
    "### 4. 4 Creating a Tensor from Existing Functions"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "FjUg7FYKXH7P",
   "metadata": {
    "id": "FjUg7FYKXH7P"
   },
   "source": [
    "There some types of uniform tensors that you would not want to create from scratch, when in fact, they are already built. \n",
    "\n",
    "Take an example of 1's tensor, 0's, and random tensors. Let's create them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "rLqjm3j1XnoD",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "rLqjm3j1XnoD",
    "outputId": "60d265ae-c583-4c66-f42a-a2a3c967b0a7"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tf.Tensor(\n",
      "[[1. 1. 1. 1.]\n",
      " [1. 1. 1. 1.]\n",
      " [1. 1. 1. 1.]\n",
      " [1. 1. 1. 1.]], shape=(4, 4), dtype=float32)\n"
     ]
    }
   ],
   "source": [
    "# Creating 1's tensor\n",
    "\n",
    "ones_tensor = tf.ones([4,4])\n",
    "\n",
    "print(ones_tensor)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "qqUg068BXv95",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "qqUg068BXv95",
    "outputId": "640b1b5e-7d8b-4209-f0bf-3e526fe04acd"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tf.Tensor([[1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]], shape=(1, 10), dtype=float32)\n"
     ]
    }
   ],
   "source": [
    "# Creating 1's tensor\n",
    "\n",
    "ones_tensor_1 = tf.ones([1,10])\n",
    "\n",
    "print(ones_tensor_1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "RzsQ6T3sX0J_",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "RzsQ6T3sX0J_",
    "outputId": "14cd3dbe-fe71-4e33-daf1-8baea6e2d176"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tf.Tensor(\n",
      "[[0. 0. 0.]\n",
      " [0. 0. 0.]\n",
      " [0. 0. 0.]], shape=(3, 3), dtype=float32)\n"
     ]
    }
   ],
   "source": [
    "# Creating zeros' tensor\n",
    "\n",
    "tensor_0 = tf.zeros([3,3])\n",
    "print(tensor_0)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3QqBFR__ZVi2",
   "metadata": {
    "id": "3QqBFR__ZVi2"
   },
   "source": [
    "We can also create a tensor with random values. During the weights initialization in neural networks, weights take random values. \n",
    "\n",
    "You might be thinking that why aren't we building neural networks now and I get you. Understanding the basics of tensors and especially working with TensorFlow is useful when it comes to creating custom neural network layers, loss functions, or optimizers. \n",
    "\n",
    "The later labs will not deal with custom layers/losses/optimizers, this is only just an introduction, so that this can serve as a reference whenever you want to take a step back into the backbone of TensorFlow high level API. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "Kv1Ro1sAYOwT",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "Kv1Ro1sAYOwT",
    "outputId": "376bfc1b-7bf0-4a59-c866-3592ca8fe5bd"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tf.Tensor(\n",
      "[[-0.43640924 -1.9633987  -0.06452483]\n",
      " [-1.056841    1.0019137   0.6735137 ]\n",
      " [ 0.06987712 -1.4077919   1.0278524 ]], shape=(3, 3), dtype=float32)\n"
     ]
    }
   ],
   "source": [
    "# Generating a tensor with random values \n",
    "\n",
    "# We first have to create a generator object\n",
    "\n",
    "rand_tensor = tf.random.Generator.from_seed(3)\n",
    "\n",
    "rand_tensor = rand_tensor.normal(shape=[3,3])\n",
    "print(rand_tensor)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ujqu7NXReuRm",
   "metadata": {
    "id": "ujqu7NXReuRm"
   },
   "source": [
    "Changing seed number in `tf.random.Generator.from_seed(3)` will change the values returned by random function. "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "J3qtTgCzfcTR",
   "metadata": {
    "id": "J3qtTgCzfcTR"
   },
   "source": [
    "We can also shuffle the existing tensor, created with `tf.constant()` or `tf.Variable()`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ilh0IwCYfn8K",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "ilh0IwCYfn8K",
    "outputId": "facadbf8-ee40-470e-9ae2-bda75024f8e7"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tf.Tensor(\n",
      "[[1 3]\n",
      " [3 4]\n",
      " [4 5]], shape=(3, 2), dtype=int32)\n"
     ]
    }
   ],
   "source": [
    "# Create a typical tensor \n",
    "\n",
    "example_tensor = tf.constant([[1,3],\n",
    "                             [3,4],\n",
    "                             [4,5]])\n",
    "\n",
    "print(example_tensor)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "FzkUNIvFhz4o",
   "metadata": {
    "id": "FzkUNIvFhz4o"
   },
   "outputs": [],
   "source": [
    "def shuffle_tensor(tensor):\n",
    "\n",
    "  \"\"\"\n",
    "  Take a tensor as input and return the shuffled tensor\n",
    "  \"\"\"\n",
    "  # Shuffle the order of the created tensor\n",
    "\n",
    "  tensor_shuffled = tf.random.shuffle(tensor)\n",
    "\n",
    "  return print(tensor_shuffled)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "oYRDOZWiiaIs",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "oYRDOZWiiaIs",
    "outputId": "07941eed-b40b-4d7a-e57a-f1063a9a8325"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tf.Tensor(\n",
      "[[4 5]\n",
      " [1 3]\n",
      " [3 4]], shape=(3, 2), dtype=int32)\n"
     ]
    }
   ],
   "source": [
    "shuffle_tensor(example_tensor)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "BxdM4e1cgtPV",
   "metadata": {
    "id": "BxdM4e1cgtPV"
   },
   "source": [
    "If you rerun the above cell more than once, you will get different orders of tensor. \n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "vOOgQsnAhN5C",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "vOOgQsnAhN5C",
    "outputId": "c5a1d085-1a22-4e0d-abcf-07142882d74d"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tf.Tensor(\n",
      "[[4 5]\n",
      " [3 4]\n",
      " [1 3]], shape=(3, 2), dtype=int32)\n"
     ]
    }
   ],
   "source": [
    "shuffle_tensor(example_tensor)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9KkhVyrlikJ8",
   "metadata": {
    "id": "9KkhVyrlikJ8"
   },
   "source": [
    "In order to prevent that, we can use `tf.random.set_seed(seed_number)` to always get the same order/values. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d9gl0SAdhCmG",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "d9gl0SAdhCmG",
    "outputId": "2e209d85-2b6e-4eaa-9c37-12a3850a9eca"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tf.Tensor(\n",
      "[[3 4]\n",
      " [4 5]\n",
      " [1 3]], shape=(3, 2), dtype=int32)\n"
     ]
    }
   ],
   "source": [
    "# Set seed \n",
    "\n",
    "tf.random.set_seed(42)\n",
    "\n",
    "shuffle_tensor(example_tensor)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0KkjtE87jBb0",
   "metadata": {
    "id": "0KkjtE87jBb0"
   },
   "source": [
    "Everytime you can run `shuffle_tensor` function with a same seed, you will get the same order. "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "LFESA8kzjJix",
   "metadata": {
    "id": "LFESA8kzjJix"
   },
   "source": [
    "You can learn more about Random number generation at [TensorFlow docs](https://www.tensorflow.org/guide/random_numbers#the_tfrandomgenerator_class). "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "UNICeI1JjnCQ",
   "metadata": {
    "id": "UNICeI1JjnCQ"
   },
   "source": [
    "<a name='4-5'></a>\n",
    "\n",
    "### 4. 5 Selecting Data in Tensor"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "JrX7DaN0kDnX",
   "metadata": {
    "id": "JrX7DaN0kDnX"
   },
   "source": [
    "We can also select values in any tensor, both single dimensional tensor and multi dimensional tensor."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "OA0uyuESjwEs",
   "metadata": {
    "id": "OA0uyuESjwEs"
   },
   "outputs": [],
   "source": [
    "# Let's create a tensor\n",
    "\n",
    "tensor_1d = tf.constant([1,2,3,4,5,6,7])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "subya0p2kbcj",
   "metadata": {
    "id": "subya0p2kbcj"
   },
   "source": [
    "Let's select multiple values in tensor created above. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "pZ_F3gikkTj3",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "pZ_F3gikkTj3",
    "outputId": "f24e3a1a-2be9-4342-e83f-57cc48c69b11"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The first value: 1\n",
      "The second value: 3\n",
      "From the 3 to 5th values: [4 5]\n",
      "From the 3 to last value: [4 5 6 7]\n",
      "The last value: 7\n",
      "Select value before the last value: 6\n",
      "Select all tensor values: [1 2 3 4 5 6 7]\n"
     ]
    }
   ],
   "source": [
    "print('The first value:', tensor_1d[0].numpy())\n",
    "print('The second value:', tensor_1d[2].numpy())\n",
    "print('From the 3 to 5th values:', tensor_1d[3:5].numpy())\n",
    "print('From the 3 to last value:', tensor_1d[3:].numpy())\n",
    "print('The last value:', tensor_1d[-1].numpy())\n",
    "print('Select value before the last value:', tensor_1d[-2].numpy())\n",
    "print('Select all tensor values:', tensor_1d[:].numpy())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "pMvoVj4plviE",
   "metadata": {
    "id": "pMvoVj4plviE"
   },
   "source": [
    "Selecting/indexing data in tensor is similar to Python list indexing, and NumPy also. \n",
    "\n",
    "Let's also select data in 2D tensor."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "AgJARJ9zl_Lm",
   "metadata": {
    "id": "AgJARJ9zl_Lm"
   },
   "outputs": [],
   "source": [
    "tensor_2d = tf.constant([[1,3],\n",
    "                          [3,4],\n",
    "                          [4,5]])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "211dhp4ymj8I",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "211dhp4ymj8I",
    "outputId": "403a2521-b4a4-4c04-a32d-21cc2fe6f40c"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The first row: [1 3]\n",
      "The second column: [3 4 5]\n",
      "The last low: [4 5]\n",
      "The first value in the last row: 4\n",
      "The last value in the last column: 5\n"
     ]
    }
   ],
   "source": [
    "print('The first row:', tensor_2d[0,:].numpy())\n",
    "print('The second column:', tensor_2d[:,1].numpy())\n",
    "print('The last low:', tensor_2d[-1,:].numpy())\n",
    "print('The first value in the last row:', tensor_2d[-1,0].numpy())\n",
    "print('The last value in the last column:', tensor_2d[-1,-1].numpy())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "Klm8JekIoemi",
   "metadata": {
    "id": "Klm8JekIoemi"
   },
   "source": [
    "<a name='4-6'></a>\n",
    "\n",
    "### 4. 6 Performing Operations on Tensors"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cylwwSN1o2QC",
   "metadata": {
    "id": "cylwwSN1o2QC"
   },
   "source": [
    "All numeric operations can be performed on tensor. Let's see few of them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "owzkLYcgpCgU",
   "metadata": {
    "id": "owzkLYcgpCgU"
   },
   "outputs": [],
   "source": [
    "# Creating example tensors \n",
    "\n",
    "tensor_1 = tf.constant([1,2,3])\n",
    "tensor_2 = tf.constant([4,5,6])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "tASu1BD7o-mN",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "tASu1BD7o-mN",
    "outputId": "38654134-1c32-47bb-8282-08855eab6755"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tf.Tensor([5 6 7], shape=(3,), dtype=int32)\n"
     ]
    }
   ],
   "source": [
    "# Adding a scalar value to a tensor\n",
    "\n",
    "print(tensor_1 + 4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "OzgmVOrApQgn",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "OzgmVOrApQgn",
    "outputId": "10630f7d-bde6-49e2-c2f6-6d36c4b3aef3"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tf.Tensor([5 7 9], shape=(3,), dtype=int32)\n"
     ]
    }
   ],
   "source": [
    "# Adding two tensors \n",
    "\n",
    "print(tensor_1 + tensor_2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "33fQGz5Rp93j",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "33fQGz5Rp93j",
    "outputId": "c57051b0-8333-41e4-cbfb-577610c4aa8c"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tf.Tensor([5 7 9], shape=(3,), dtype=int32)\n"
     ]
    }
   ],
   "source": [
    "# Can also add with tf.add() or tf.math.add()\n",
    "\n",
    "print(tf.add(tensor_1, tensor_2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "GHomJRJUpcwx",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "GHomJRJUpcwx",
    "outputId": "405d4223-ff55-40b7-f935-20480438b11f"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tf.Tensor([ 4 10 18], shape=(3,), dtype=int32)\n"
     ]
    }
   ],
   "source": [
    "# multiplying tensors with tf.multiply()\n",
    "\n",
    "print(tf.multiply(tensor_1, tensor_2))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "LP_-cNpQqZ_s",
   "metadata": {
    "id": "LP_-cNpQqZ_s"
   },
   "source": [
    "You can learn more at official docs, [`tf.math()`](https://www.tensorflow.org/api_docs/python/tf/math) specifically. Almost all maths operations can be done on tensors."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "FCscqPnoqxPN",
   "metadata": {
    "id": "FCscqPnoqxPN"
   },
   "source": [
    "<a name='4-7'></a>\n",
    "\n",
    "### 4. 7 Manipulating the Shape of Tensor"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "KNzd4_Frq-Cc",
   "metadata": {
    "id": "KNzd4_Frq-Cc"
   },
   "source": [
    "There are times you would want to reshape a tensor. Here is how to go about it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e-AyfugirGDA",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "e-AyfugirGDA",
    "outputId": "f634a3ee-77ae-4f48-8dea-609005e27e7a"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tf.Tensor(\n",
      "[[1 3]\n",
      " [3 4]\n",
      " [4 5]], shape=(3, 2), dtype=int32)\n"
     ]
    }
   ],
   "source": [
    "print(example_tensor)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "y6xE4VaNrP7G",
   "metadata": {
    "id": "y6xE4VaNrP7G"
   },
   "source": [
    "Let's reshape the above tensor into `(2,3)`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "-1-Ed2bgrLB-",
   "metadata": {
    "id": "-1-Ed2bgrLB-"
   },
   "outputs": [],
   "source": [
    "tens_reshaped = tf.reshape(example_tensor, [2,3])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "BwfGZSqWrmJt",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "BwfGZSqWrmJt",
    "outputId": "8310b112-53fb-4478-d62f-1cf6ff812184"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tf.Tensor(\n",
      "[[1 3 3]\n",
      " [4 4 5]], shape=(2, 3), dtype=int32)\n"
     ]
    }
   ],
   "source": [
    "print(tens_reshaped)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "IxAznPNRrwn1",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "IxAznPNRrwn1",
    "outputId": "143f00b6-3c7f-4381-e9e7-906aa9d853ed"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tf.Tensor(\n",
      "[[1]\n",
      " [3]\n",
      " [3]\n",
      " [4]\n",
      " [4]\n",
      " [5]], shape=(6, 1), dtype=int32)\n"
     ]
    }
   ],
   "source": [
    "# Also to (6,1)\n",
    "\n",
    "print(tf.reshape(example_tensor, [6,1]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 99,
   "id": "FEAp1dU8sCMs",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "FEAp1dU8sCMs",
    "outputId": "39affc67-c8b2-4298-ef74-9a74fb3eee57"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[3, 2]\n"
     ]
    }
   ],
   "source": [
    "# You can also shape a tensor into a list\n",
    "\n",
    "print(example_tensor.shape.as_list())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 100,
   "id": "lZH3nEBdsibi",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "lZH3nEBdsibi",
    "outputId": "19ccb6db-b6f8-4abb-a4cc-14638f34c5ec"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tf.Tensor([1 3 3 4 4 5], shape=(6,), dtype=int32)\n"
     ]
    }
   ],
   "source": [
    "# You can also flatten a tensor\n",
    "\n",
    "print(tf.reshape(example_tensor, [-1]))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "pJPpqTGgs8OR",
   "metadata": {
    "id": "pJPpqTGgs8OR"
   },
   "source": [
    "There are rules to reshaping a tensor. The new shape has to be resonable. Take an example below, it would create an erroe because there is no way you can reshape the `example_tensor into (5,5).`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 101,
   "id": "TICgTu35tSn1",
   "metadata": {
    "id": "TICgTu35tSn1"
   },
   "outputs": [],
   "source": [
    "# Running the cell below will create an error\n",
    "\n",
    "# print(tf.reshape(example_tensor, [5,5]))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "oP6_2Vpltfpn",
   "metadata": {
    "id": "oP6_2Vpltfpn"
   },
   "source": [
    "*******************"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6kIIvdnLt0L_",
   "metadata": {
    "id": "6kIIvdnLt0L_"
   },
   "source": [
    "That's it for the introduction to TensorFlow and the basics of tensors. 'TensorFlow API revolves around tensors'`(CC: Aurelion Geron)`, and that's why we didn't get straight to doing big things with TF high level API immediately without looking into what makes them possible. "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "i52l7HmBuSdi",
   "metadata": {
    "id": "i52l7HmBuSdi"
   },
   "source": [
    "## [BACK TO TOP](#0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "88rLNK1KuUkY",
   "metadata": {
    "id": "88rLNK1KuUkY"
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "colab": {
   "collapsed_sections": [],
   "name": "7.2 Intro to TensorFlow for Deep Learning.ipynb",
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3.7.10 64-bit ('tensor': conda)",
   "language": "python",
   "name": "python3710jvsc74a57bd034ac5db714c5906ee087fcf6e2d00ee4febf096586592b6ba3662ed3b7e7a5f6"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
